package com.meirengu.utils;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.SocketTimeoutException;
import java.util.Iterator;
import java.util.Map;

/**
 * Http Client完整封装的工具类
 * 提供在Java中调用http资源的方法,可以替换webservice
 *
 * @author dzj
 */
public class HttpClientInvoker {
    private Log logger = LogFactory.getLog(HttpClientInvoker.class);
    private static HttpClientInvoker httpInvoker = new HttpClientInvoker();
    private HttpClient client = null;
    private String charset = "UTF-8";
    private int timeout = 100000;//超时时间单位毫秒
    private boolean useProxy = false;
    private String proxyHost = null;
    private int proxyPort;
    private String proxyUsername = null;
    private String proxyPassword = null;
    private boolean initialized = false;

    public static HttpClientInvoker getInstance() {
        return httpInvoker;
    }

    private HttpClientInvoker() {

        MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();

        HttpConnectionManagerParams params = manager.getParams();

        params.setConnectionTimeout(10000); // 如果没有连接上就触发超时结束连接，毫秒
        params.setSoTimeout(timeout);  // 连上后，等待数据返回最多等多久，毫秒

        params.setDefaultMaxConnectionsPerHost(32);//very important!! 单个主机最大连接数
        params.setMaxTotalConnections(256);//very important!!  最大总连接

        client = new HttpClient(manager);
        client.getParams().setParameter("http.protocol.content-charset", charset);
        client.getParams().setContentCharset(charset);
    }

    public HttpClientInvoker(String charset, int timeout, boolean useProxy,
                             String proxyHost, int proxyPort, String proxyUsername,
                             String proxyPassword) {
        client = new HttpClient(new MultiThreadedHttpConnectionManager());
        if (charset != null && !charset.trim().equals("")) {
            this.charset = charset;
        }
        if (timeout > 0) {
            this.timeout = timeout;
        }
        client.getParams().setParameter("http.protocol.content-charset", charset);
        client.getParams().setContentCharset(charset);
        client.getParams().setSoTimeout(timeout);
        if (useProxy && proxyHost != null &&
                !proxyHost.trim().equals("") && proxyPort > 0) {
            HostConfiguration hc = new HostConfiguration();
            hc.setProxy(proxyHost, proxyPort);
            client.setHostConfiguration(hc);
            if (proxyUsername != null && !proxyUsername.trim().equals("") &&
                    proxyPassword != null && !proxyPassword.trim().equals("")) {
                client.getState().setProxyCredentials(AuthScope.ANY,
                        new UsernamePasswordCredentials(proxyUsername, proxyPassword));
            }
        }
        initialized = true;
        logger.debug("HttpInvoker初始化完成");
    }

    public synchronized void init() {
        if (charset != null && !charset.trim().equals("")) {
            client.getParams().setParameter("http.protocol.content-charset", charset);
            client.getParams().setContentCharset(charset);
        }
        if (timeout > 0) {
            client.getParams().setSoTimeout(timeout);
        }
        if (useProxy && proxyHost != null &&
                !proxyHost.trim().equals("") && proxyPort > 0) {
            HostConfiguration hc = new HostConfiguration();
            hc.setProxy(proxyHost, proxyPort);
            client.setHostConfiguration(hc);
            if (proxyUsername != null && !proxyUsername.trim().equals("") &&
                    proxyPassword != null && !proxyPassword.trim().equals("")) {
                client.getState().setProxyCredentials(AuthScope.ANY,
                        new UsernamePasswordCredentials(proxyUsername, proxyPassword));
            }
        }
        initialized = true;
        logger.debug("HttpInvoker初始化完成");
    }

    public String invoke(String url) throws Exception {
        return invoke(url, null, false);
    }

    /**
     * 解决乱码问题
     *
     * @author dzj
     */
    public static class UTF8PostMethod extends PostMethod {
        public UTF8PostMethod(String url) {
            super(url);
        }

        @Override
        public String getRequestCharSet() {
            //return super.getRequestCharSet();
            return "UTF-8";
        }
    }

    //params中传入非String数据时可能会报错-连接url失败 保险期间全传String
    public String invoke(String url, Map params, boolean isPost) throws Exception {
        logger.debug("HTTP调用[" + (isPost ? "POST" : "GET") + "][" + url + "][" + ((params + "").length() > 500 ? (params + "").substring(0, 500) : params) + "]");
        HttpMethod httpMethod = null;
        String result = "";
        try {
            if (isPost && params != null && params.size() > 0) {
                Iterator paramKeys = params.keySet().iterator();
                httpMethod = new UTF8PostMethod(url);
                NameValuePair[] form = new NameValuePair[params.size()];
                int formIndex = 0;
                while (paramKeys.hasNext()) {
                    String key = (String) paramKeys.next();
                    Object value = params.get(key);
                    if (value != null && value instanceof String && !value.equals("")) {
                        form[formIndex] = new NameValuePair(key, (String) value);
                        formIndex++;
                    } else if (value != null && value instanceof String[] &&
                            ((String[]) value).length > 0) {
                        NameValuePair[] tempForm =
                                new NameValuePair[form.length + ((String[]) value).length - 1];
                        for (int i = 0; i < formIndex; i++) {
                            tempForm[i] = form[i];
                        }
                        form = tempForm;
                        for (String v : (String[]) value) {
                            form[formIndex] = new NameValuePair(key, (String) v);
                            formIndex++;
                        }
                    }
                }
                ((PostMethod) httpMethod).setRequestBody(form);
            } else {
                if (params != null && params.size() > 0) {
                    Iterator paramKeys = params.keySet().iterator();
                    StringBuffer getUrl = new StringBuffer(url.trim());
                    if (url.trim().indexOf("?") > -1) {
                        if (url.trim().indexOf("?") < url.trim().length() - 1 &&
                                url.trim().indexOf("&") < url.trim().length() - 1) {
                            getUrl.append("&");
                        }
                    } else {
                        getUrl.append("?");
                    }
                    while (paramKeys.hasNext()) {
                        String key = (String) paramKeys.next();
                        Object value = params.get(key);
                        if (value != null && value instanceof String && !value.equals("")) {
                            getUrl.append(key).append("=").append(value).append("&");
                        } else if (value != null && value instanceof String[] &&
                                ((String[]) value).length > 0) {
                            for (String v : (String[]) value) {
                                getUrl.append(key).append("=").append(v).append("&");
                            }
                        }
                    }
                    if (getUrl.lastIndexOf("&") == getUrl.length() - 1) {
                        httpMethod = new GetMethod(getUrl.substring(0, getUrl.length() - 1));
                    } else {
                        httpMethod = new GetMethod(getUrl.toString());
                    }
                } else {
                    httpMethod = new GetMethod(url);
                }
            }
            client.executeMethod(httpMethod);
//            result = httpMethod.getResponseBodyAsString();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    httpMethod.getResponseBodyAsStream(), "UTF-8"));
            String line = null;
            String html = null;
            while ((line = reader.readLine()) != null) {
                if (html == null) {
                    html = "";
                } else {
                    html += "\r\n";
                }
                html += line;
            }
            if (html != null) {
                result = new String(html.getBytes("UTF-8"), charset);
            }
        } catch (SocketTimeoutException e) {
            logger.error("连接超时[" + url + "]");
            throw e;
        } catch (java.net.ConnectException e) {
            logger.error("连接失败[" + url + "]");
            throw e;
        } catch (Exception e) {
            logger.error("连接时出现异常[" + url + "]");
            throw e;
        } finally {
            if (httpMethod != null) {
                try {
                    httpMethod.releaseConnection();
                } catch (Exception e) {
                    logger.error("释放网络连接失败[" + url + "]");
                    throw e;
                }
            }
        }

        return result;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setProxyHost(String proxyHost) {
        this.proxyHost = proxyHost;
    }

    public void setProxyPort(int proxyPort) {
        this.proxyPort = proxyPort;
    }

    public void setProxyUsername(String proxyUsername) {
        this.proxyUsername = proxyUsername;
    }

    public void setProxyPassword(String proxyPassword) {
        this.proxyPassword = proxyPassword;
    }

    public void setUseProxy(boolean useProxy) {
        this.useProxy = useProxy;
    }

    public synchronized boolean isInitialized() {
        return initialized;
    }
}
